package com.heima.schedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.common.constants.ScheduleConstants;
import com.heima.common.redis.RedisCacheService;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.mapper.TaskinfoLogsMapper;
import com.heima.schedule.mapper.TaskinfoMapper;
import com.heima.schedule.service.TaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.Set;

@Service
public class TaskServiceImpl {
	@Autowired
	private TaskinfoMapper taskinfoMapper;
	@Autowired
	private TaskinfoLogsMapper taskinfoLogsMapper;
	@Autowired
	private RedisCacheService redisCacheService;

	/**
	 * 保存任务
	 *
	 * @param task
	 */

	public void addTask(Task task) {
		//将任务添加到数据库
		Boolean flag = saveTaskToDB(task);
		if (flag) {
			saveTaskToRedis(task);
		}
	}

	/**
	 * 保存数据到数据库表中
	 *
	 * @param task
	 */
	private void saveTaskToRedis(Task task) {
		//把任务添加到redis中
		String keySuffix = task.getTaskType() + "_" + task.getPriority();
		//获取未来五分钟
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.MINUTE, 5);
		long timeInMillis = calendar.getTimeInMillis();
		//当任务的执行时间小于系统时间时，就把它存到list集合中
		if (task.getExecuteTime() <= System.currentTimeMillis()) {
			redisCacheService.lLeftPush(ScheduleConstants.TOPIC + keySuffix, JSON.toJSONString(task));
		} else
			//当任务的执行时间大于系统时间，且小于预设时间五分钟，就把它存到zset集合中
			if (task.getExecuteTime() > System.currentTimeMillis() && task.getExecuteTime() <= timeInMillis) {
				redisCacheService.zAdd(ScheduleConstants.FUTURE + keySuffix, JSON.toJSONString(task), task.getExecuteTime());
			}
	}

	private Boolean saveTaskToDB(Task task) {
		//将任务保存到taskinfo表中
		Taskinfo taskinfo = new Taskinfo();
		BeanUtils.copyProperties(task, taskinfo);
		taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
		taskinfoMapper.insert(taskinfo);
		//给Task类中的id赋值
		task.setTaskId(taskinfo.getTaskId());
		//将数据保存到taskinfolog表中
		TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
		BeanUtils.copyProperties(taskinfo, taskinfoLogs);
		taskinfoLogs.setVersion(1);
		taskinfoLogs.setStatus(0);
		int insert = taskinfoLogsMapper.insert(taskinfoLogs);
		return insert == 1;
	}

	/**
	 * 获取数据，消费数据
	 * 在redis中操作
	 *
	 * @param taskType
	 * @param priority
	 * @return
	 */

	private Task pullTask(Integer taskType, Integer priority) {
		//定义key
		String keySuffix = taskType + "_" + priority;
		//从redis中的list中获取数据，并且删除数据
		String taskStr = redisCacheService.lRightPop(ScheduleConstants.TOPIC, keySuffix);
		Task task = JSON.parseObject(taskStr, Task.class);
		//删除任务表
		taskinfoMapper.deleteById(task.getTaskId());
		//修改任务日志
		TaskinfoLogs taskinfoLogs = taskinfoLogsMapper.selectById(task.getTaskId());
		if (taskinfoLogs != null) {
			//这里应该要修改version，但是已经集成mybatisPlus的乐观锁
			//修改状态
			taskinfoLogs.setStatus(ScheduleConstants.EXECUTED);
			taskinfoLogsMapper.updateById(taskinfoLogs);
		}
		//返回数据
		return task;
	}
	/**
	 * 定时将zset中的数据更新同步到list中
	 * 没有人调用这个方法不用写返回值
	 */

	@Scheduled(cron = "0 0/1 * * * ? ")
	public void refresh() {
		String token =  redisCacheService.tryLock("FUTURE_TASK_SYNC", 30 * 1000);
		//已经拿到了锁，执行任务
		if (StringUtils.isNoneBlank(token)) {
			System.out.println(System.currentTimeMillis() / 1000 + "执行定时任务");
			//获取未来数据的key
			Set<String> keys = redisCacheService.scan(ScheduleConstants.FUTURE + "*");
			if (keys != null && keys.size() > 0) {
				for (String key : keys) {
					//根据key获取对应的value
					Set<String> values = redisCacheService.zRangeByScore(key, 0, System.currentTimeMillis());
					//同步数据到list中
					String keySuffix = key.split(ScheduleConstants.FUTURE)[1];
					String topickey = ScheduleConstants.TOPIC + keySuffix;
					redisCacheService.refreshWithPipeline(key, topickey, values);
					System.out.println("成功将" + key + "下的当前需要执行的任务刷新到" + topickey);
				}
			}
		}
	}
}
